﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FifaChamps.API.Data;


namespace FifaChamps.API.Logic
{
    public abstract class Phase
    {
        public int ID { get; set; }
        public int StageID { get; set; }
        public string Name { get; set; }      

        internal abstract int Add();

        public abstract int Add_PhasePlayer(int playerID);

        public abstract Dictionary<int, PhasePlayer> Get_PhasePlayers();

        #region Methods Static        

        public static int Add_Phase(int stageID, StageType stageType, params PhaseParam[] parameters)
        {           
            int phaseid = 0;
            //collect the base params
            string name = parameters.Where( x => x.Name.ToLower().Trim().Equals("name")).First().Value as string;
            switch (stageType)
            {
                case StageType.Season:                    
                    DateTime? season_date = default(DateTime?);
                    string season_venue = default(string);
                    if (parameters.Where(x => x.Name.ToLower().Trim().Equals("date")).Any()) season_date = parameters.Where(x => x.Name.ToLower().Trim().Equals("date")).First().Value as DateTime?;
                    if (parameters.Where(x => x.Name.ToLower().Trim().Equals("venue")).Any()) season_venue = parameters.Where(x => x.Name.ToLower().Trim().Equals("venue")).First().Value as string;
                    var phase_season = StagePhase_Season.Factory(stageID, name, season_date, season_venue);
                    phaseid = phase_season.Add();
                    break;
                case StageType.Group:
                    DateTime? gr_date = default(DateTime?);
                    string gr_venue = default(string);
                    if (parameters.Where(x => x.Name.ToLower().Trim().Equals("date")).Any()) gr_date = parameters.Where(x => x.Name.ToLower().Trim().Equals("date")).First().Value as DateTime?;
                    if (parameters.Where(x => x.Name.ToLower().Trim().Equals("venue")).Any()) gr_venue = parameters.Where(x => x.Name.ToLower().Trim().Equals("venue")).First().Value as string;
                    var phase_group = StagePhase_Group.Factory(stageID, name);
                    phaseid = phase_group.Add();
                    break;
                case StageType.Knockout:
                    var knockout_phase = StagePhase_Knockout.Factory(stageID, name);
                    phaseid = knockout_phase.Add();
                    break;
            }

            return phaseid;
        }

        public static Phase Get_One(int phaseID, StageType stagetype)
        {
            switch (stagetype)
            {
                case StageType.Knockout:
                    return StagePhase_Knockout.Get_One(phaseID);
                    break;
                case StageType.Group:
                    return StagePhase_Group.Get_One(phaseID);
                    break;
            }
            
            return null;
        }

        #endregion
    }

    public class StagePhase_Season : Phase
    {
        public DateTime? StartDate { get; set; }
        public string Venue { get; set; }

        #region Constructor's and Factory        
        private StagePhase_Season(int id, int stageid, string name, DateTime? date, string venue)
        {
            base.ID = id;
            base.StageID = stageid;
            base.Name = name;
            this.StartDate = date;
            this.Venue = venue;
        }

        public static StagePhase_Season Factory(int stageid, string name, DateTime? date, string venue)
        {
            return Factory(0, stageid, name, date, venue);
        }

        public static StagePhase_Season Factory(int id, int stageid, string name, DateTime? date, string venue)
        {
            return new StagePhase_Season(id, stageid, name, date, venue);
        }
        #endregion

        internal override int Add()
        { 
            if (this.ID != default(int))
            {
                return Add_SeasonPhase(this.StageID, this.Name, this.StartDate, this.Venue);
            }
            return default(int);
        }        

        internal static int Add_SeasonPhase(int stageid, string name, DateTime? date, string venue)
        {
            return DataManager.Add_Season(stageid, name, date, venue);
        }

        public override int Add_PhasePlayer(int playerID)
        {
            throw new NotImplementedException();
        }

        public override Dictionary<int, PhasePlayer> Get_PhasePlayers()
        {
            throw new NotImplementedException();
        }
    }

    public class StagePhase_Group : Phase
    {
        #region Constructor's and Factory        
        private StagePhase_Group(int id, int stageid, string name)
        {
            base.ID = id;
            base.StageID = stageid;
            base.Name = name;
        }

        public static StagePhase_Group Factory(int stageid, string name)
        {
            return Factory(0, stageid, name);
        }

        public static StagePhase_Group Factory(int id, int stageid, string name)
        {
            return new StagePhase_Group(id, stageid, name);
        }
        #endregion

        internal override int Add()
        {
            return Add_GroupPhase(this.StageID, this.Name);
        }

        public override Dictionary<int, PhasePlayer> Get_PhasePlayers()
        {
            Dictionary<int, PhasePlayer> players = new Dictionary<int, PhasePlayer>();
            var group_players = GroupPlayer.Get_GroupPhase_Players(this.ID);
            foreach (var player in group_players)
            {
                players.Add(player.PhasePlayerID, player);
            }

            return players;
        }

        public override int Add_PhasePlayer(int playerID)
        {
            if (this.ID > 0)
            {
                var group_player = PhasePlayer.Factory<GroupPlayer>(playerID);
                group_player.Add_PhasePlayer(this.ID);
            }
            return 0;
        }

        internal static int Add_GroupPhase(int stageid, string name)
        {
            return DataManager.Add_Group(stageid, name);
        }

        internal static StagePhase_Group Get_One(int phaseID)
        {
            var group = DataManager.Get_Group(phaseID);
            var group_phase = Factory(group.GroupID, group.StageID, group.Name);

            return group_phase;
        }
    }

    public class StagePhase_Knockout : Phase
    {
        #region Constructor's and Factory        
        private StagePhase_Knockout(int id, int stageid, string name)
        {
            base.ID = id;
            base.StageID = stageid;
            base.Name = name;
        }

        public static StagePhase_Knockout Factory(int stageid, string name)
        {
            return Factory(0, stageid, name);
        }

        public static StagePhase_Knockout Factory(int id, int stageid, string name)
        {
            return new StagePhase_Knockout(id, stageid, name);
        }
        #endregion

        internal override int Add()
        {
            return Add_KnockoutPhase(this.StageID, this.Name);
        }

        public override int Add_PhasePlayer(int playerID)
        {
            if (this.ID > 0)
            {
                var ko_player = PhasePlayer.Factory<KnockoutPlayer>(playerID);
                ko_player.Add_PhasePlayer(this.ID);
            }
            return 0;
        }

        public override Dictionary<int, PhasePlayer> Get_PhasePlayers()
        {
            Dictionary<int, PhasePlayer> players = new Dictionary<int, PhasePlayer>();
            var ko_players = KnockoutPlayer.Get_KnockoutPhase_Players(this.ID);
            foreach (var player in ko_players)
            {
                players.Add(player.PhasePlayerID, player);
            }

            return players;
        }

        internal static StagePhase_Knockout Get_One(int phaseID)
        {
            var knockout = DataManager.Get_Knockout(phaseID);
            var ko_phase = Factory(knockout.KnockoutID, knockout.StageID, knockout.Name);
            
            return ko_phase;
        }

        internal static int Add_KnockoutPhase(int stageid, string name)
        {
            return DataManager.Add_Knockout(stageid, name);
        }
    }

    public struct PhaseParam
    {
        public string Name {get; private set;}
        public object Value {get; private set;}

        private PhaseParam(string name, string value): this()
        {
            Name = name;
            Value = value;
        }

        public static PhaseParam Factory(string name, string value)
        {
            return new PhaseParam(name, value);
        }
    }    
}
